package net.larsan.dconf;

import static org.apache.commons.lang.Validate.notNull;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import net.larsan.dconf.util.ExtendedValidate;
import net.larsan.dconf.util.PropertiesExporter;
import net.larsan.dconf.util.PropertiesImporter;

/**
 * Base class for real implementation. This class takes care of most 
 * methods. The subclass does not have to make the attribute
 * map or the child list immutable, as this class will take care of 
 * that.
 * 
 * @author Lars J. Nilsson
 */
public abstract class DNodeSpi implements DNode {
	
	protected final DNodeStore store;
	protected final DPath path;
	
	/**
	 * @param store Store to use, must not be null
	 * @param path Node path, must not be null
	 */
	protected DNodeSpi(DNodeStore store, DPath path) {
		notNull(store);
		notNull(path);
		this.store = store;
		this.path = path;
	}
	
	@Override
	public DNodeStore getStore() {
		return store;
	}

	@Override
	public Map<String, String> getAttributes() {
		return Collections.unmodifiableMap(spiGetAttributes());
	}

	@Override
	public List<DNode> getChildren() {
		return Collections.unmodifiableList(spiGetChildren());
	}

	@Override
	public DNode getParent() {
		DPath p = path.getParent();
		if(p == null) {
			return null;
		} else {
			return spiGetParent(p); 
		}
	}

	@Override
	public DPath getPath() {
		return path;
	}
	
	@Override
	public String getAttribute(String key) {
		notNull(key);
		return spiGetAttributes().get(key);
	}
	
	@Override
	public DNode getChild(String name, boolean create) {
		notNull(name);
		DPath path = getPath().child(name);
		return store.get(path, create);
	}
	
	@Override
	public void remove() {
		store.destroy(path);
	}
	
	@Override
	public boolean hasAttribute(String key) {
		notNull(key);
		return spiGetAttributes().containsKey(key);
	}
	
	@Override
	public String removeAttribute(String key) {
		notNull(key);
		return store.remove(getPath(), key);
	}
	
	public String setAttribute(String key, String val) {
		ExtendedValidate.notNull(key, val);
		return store.set(getPath(), key, val);
	}
	
	@Override
	public void importProperties(Properties p) {
		notNull(p);
		PropertiesImporter importer = new PropertiesImporter(this, true);
		importer.performImport(p);
	}
	
	@Override
	public Properties exportAsProperties() {
		PropertiesExporter exporter = new PropertiesExporter();
		visit(exporter);
		return exporter.getTarget();
	}

	@Override
	public void visit(DNodeVisitor visitor) {
		notNull(visitor);
		visitor.visit(this);
	}
	
	
	// --- PROTECTED METHODS --- //

	/**
	 * Get all attributes. This map does not have to be immutable
	 * as it will be wrapped before passing on. 
	 */
	protected abstract Map<String, String> spiGetAttributes();

	/**
	 * Get all children. This list does not have to be immutable
	 * as it will be wrapped before passing on. 
	 */
	protected abstract List<DNode> spiGetChildren();
	
	/**
	 * Get the parent node, or null for root nodes. 
	 */
	protected abstract DNode spiGetParent(DPath p);

}
